using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// ���Ͷ���أ�֧���κ�ʵ��IPoolable�����ͣ�
/// </summary>
/// <typeparam name="T">��������</typeparam>
public class GenericObjectPool<T> where T : class,IPoolable,new()
{
    private Queue<T> m_Pool = new Queue<T>();

    private int m_MaxSize;
    private int m_PrewarmCount;
    private Transform m_ParentTransform;


    public int TotalCount { get; private set; }
    public int ActiveCount => TotalCount - m_Pool.Count;

    public int InactiveCount=>m_Pool.Count;

    public GenericObjectPool(int prewarmCount=10,int maxSize = 100,Transform parent=null)
    {
        m_PrewarmCount = prewarmCount;
        m_MaxSize = maxSize;
        m_ParentTransform = parent;
        Prewarm();
    }

    private void Prewarm()
    {
        for (int index = 0; index < m_PrewarmCount; index++)
        {
            ReturnToPool(CreateNew());
        }
    }

    public T Get()
    {
        T obj = null;
        if (m_Pool.Count > 0)
            obj = m_Pool.Dequeue();
        else
        {
            if (TotalCount>=m_MaxSize)
            {
                Logger.LogWarning($"���������({typeof(T)})");
                return null;
            }

            obj = m_Pool.Dequeue();
        }

        if (obj is MonoBehaviour behaviour)
        {
            behaviour.gameObject.SetActive(true);
        }

        obj.OnSpawn();
        return obj;
    }

    public void ReturnToPool(T obj)
    {
        if (obj == null) return;
        if (m_Pool.Count > m_MaxSize)
        {
            DestroyObject(obj);
            return;
        }

        if(obj is MonoBehaviour behaviour)
        {
            behaviour.gameObject.SetActive(false);
            if (m_ParentTransform != null)
            {
                behaviour.transform.SetParent(m_ParentTransform);
            }
        }

        obj.OnDespawn();
        m_Pool.Enqueue(obj);
    }


    protected T CreateNew()
    {
        T newObj = null;

        if (typeof(MonoBehaviour).IsAssignableFrom(typeof(T)))
        {
            GameObject go = new GameObject($"{typeof(T).Name}_Pooled");
            newObj = go.AddComponent(typeof(T)) as T;
        }
        else
        {
            newObj = new T();
        }

        TotalCount++;
        return newObj;
    }

    private void DestroyObject(T obj) 
    {
        if (obj is MonoBehaviour behaviour) 
        { 
          Object.Destroy(behaviour.gameObject);
        }

        TotalCount--;
    }

    public void Clear()
    {
        while (m_Pool.Count > 0)
        {
            DestroyObject(m_Pool.Dequeue());
        }
    }
}

/// <summary>
/// ��չ������֧�� Vector3 ����λ��
/// </summary>
public static class ObjectPoolExtensions
{
    public static T Get<T>(this GenericObjectPool<T> pool, Vector3 position) where T : class, IPoolable, new()
    {
        T obj = pool.Get();
        if (obj is MonoBehaviour monoObj)
        {
            monoObj.transform.position = position;
        }
        return obj;
    }
}
